package cn.kwq.project_manger.service.impl;

import cn.kwq.project_manger.dtoUtils.SwitchConvDtoUtils;
import cn.kwq.project_manger.pojo.Conv;
import cn.kwq.project_manger.pojo.Msg;
import cn.kwq.project_manger.pojo.QConv;
import cn.kwq.project_manger.pojo.QMsg;
import cn.kwq.project_manger.pojo.dto.ConvDto;
import cn.kwq.project_manger.pojo.dto.ConvLRDto;
import cn.kwq.project_manger.pojo.dto.ResMsgDto;
import cn.kwq.project_manger.pojo.dto.SendMsg;
import cn.kwq.project_manger.repostitory.ConvRepository;
import cn.kwq.project_manger.repostitory.MessageRepository;
import cn.kwq.project_manger.service.MsgService;

import com.querydsl.jpa.impl.JPAQueryFactory;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static java.util.Comparator.comparing;

@Service
public class MsgServiceImpl implements MsgService {

    @Autowired
    MessageRepository msgRepository;

    @Autowired
    JPAQueryFactory jpaQueryFactory;

    @Autowired
    ConvRepository convRepository;

    @Autowired
    SwitchConvDtoUtils switchUtils;

    @Override
    public Msg Send(SendMsg sendMsg) {
        return msgRepository.save(sendMsg.getMsg());
    }

    @Override
    public List<ResMsgDto> getMsg(Long userId) {
        QMsg msg=QMsg.msg1;
        List<Msg> fetch = jpaQueryFactory.select(msg)
                .from(msg)
                .where(msg.toId.eq(userId))
                .fetch();
        return fetch
                .stream()
                .parallel()
                .map(this::Msg2Res)
                .collect(Collectors.toList());

    }

    private ResMsgDto Msg2Res(Msg item) {
        QMsg msg=QMsg.msg1;
        Date date = jpaQueryFactory.select(msg._super.createdDate)
                .from(msg)
                .where(msg.msgId.eq(item.getMsgId()))
                .fetchOne();
        return ResMsgDto
                .builder()
                .createDate(date)
                .msg(item)
                .build();
    }


    @Override
    @Transactional
    public Msg Read(Long msgId) {

        QMsg msg=QMsg.msg1;
        jpaQueryFactory.update(msg)
                .set(msg.isRead,1)
                .where(msg.msgId.eq(msgId))
                .execute();

        return  jpaQueryFactory.selectFrom(msg)
                .where(msg.msgId.eq(msgId))
                .fetchOne();

    }

    @Override
    public Long CountUnRead(Long userId) {
        QMsg msg=QMsg.msg1;

        return jpaQueryFactory.select(msg.count())
                .from(msg)
                .where(msg.toId.eq(userId))
                .where(msg.isRead.eq(0))
                .fetchOne();
    }

    //锁对象
    final Object lock=new Object();
    @Deprecated
    @Override
    public ConvDto SendConv(Long fromId, Long toId, String msg) {
        QConv conv=QConv.conv;
        Long maxConId = jpaQueryFactory.select(conv.convId.max())
                .from(conv)
                .fetchOne();
        if (maxConId==null){
            maxConId=1L;
        } else {
            synchronized (lock){
                maxConId++;
            }
        }
        //如果他们之间存在对话
        Long exitNum = jpaQueryFactory.select(conv.convMsgId.count())
                .from(conv)
                .where((conv.fromId.eq(fromId).and(conv.toId.eq(toId)))
                        .or(conv.fromId.eq(toId).and(conv.toId.eq(fromId))))
                .fetchOne();
        if (exitNum!=null&&exitNum!=0){
        maxConId = jpaQueryFactory.selectDistinct(conv.convId)
                    .from(conv)
                    .where((conv.fromId.eq(fromId).and(conv.toId.eq(toId)))
                        .or(conv.fromId.eq(toId).and(conv.toId.eq(fromId))))
                    .fetchOne();
        }
        if (maxConId==null){
            throw new RuntimeException("对话算法错误");
        }

        convRepository.save(Conv
                .builder()
                .fromId(fromId)
                .toId(toId)
                .convId(maxConId)
                .msg(msg)
                .isRead(0)
                .createTime(new Date())
                .build());

        List<Conv> fetch = jpaQueryFactory.selectFrom(conv)
                .where(conv.convId.eq(maxConId))
                .orderBy(conv._super.createdDate.asc(),conv._super.createdDate.asc())
                .fetch();

        List<ConvLRDto> convLRDtoList=switchUtils.Conv2LR(fromId,fetch);
        ConcurrentHashMap<Long,List<ConvLRDto>> map=new ConcurrentHashMap<>();
        map.put(maxConId,convLRDtoList);

        return ConvDto
                .builder()
                .convDto(map)
                .build();
    }

    @Deprecated
    @Override
    public ConvDto GetOneConMsg(Long fromId, Long toId) {
        QConv conv=QConv.conv;
        List<Conv> fetch = jpaQueryFactory.selectFrom(conv)
                .where((conv.fromId.eq(fromId).and(conv.toId.eq(toId)))
                        .or(conv.fromId.eq(toId).and(conv.toId.eq(fromId))))
                .orderBy(conv._super.createdDate.asc(), conv._super.createdDate.asc())
                .fetch();

        if (fetch == null||fetch.size()==0){
            return null;
        }
        List<ConvLRDto> convLRDtoList =switchUtils.Conv2LR(fromId,fetch);
        ConcurrentHashMap<Long,List<ConvLRDto>> map=new ConcurrentHashMap<>();
        map.put(fetch.get(0).getConvId(),convLRDtoList);

        return ConvDto
                .builder()
                .convDto(map)
                .build();
    }

    @Deprecated
    @Override
    public ConvDto GetAllConvMsg(Long userId) {
        QConv conv=QConv.conv;
        List<Conv> fetch = jpaQueryFactory.selectFrom(conv)
                .where(conv.fromId.eq(userId).or(conv.toId.eq(userId)))
                .orderBy(conv._super.createdDate.asc(), conv._super.updateDate.asc())
                .fetch();
        return switchUtils.ConvDto2List(userId,fetch);
    }
    private Long unReadCount(List<Conv> convs) {
        return   convs.stream().filter(i -> i.getIsRead() == 0).count();
    }


}
